using System;
using System.Threading;
using OFX.Aggregator.Infrastructure;

namespace OFX.Aggregator.WebScraping.Infrastructure
{
	public abstract class WebStatementRetrievalAsyncResult : IAsyncResult, IHtmlPageProcessorFinished, IDisposable
	{
		protected WebStatementRetrievalAsyncResult(
			AsyncCallback callback, IStatementProcessingWorkflow workflow, IHtmlPageProcessor[] htmlProcessors)
		{
			this.htmlProcessors = htmlProcessors;

			this.callback = callback;
			this.workflow = workflow;
		}

		~WebStatementRetrievalAsyncResult()
		{
			Dispose(false);
		}

		protected virtual void Dispose(bool disposing)
		{
			if (disposing)
			{
				if (this.statementRetrievalCompletedEvent != null)
				{
					this.statementRetrievalCompletedEvent.Close();
					this.statementRetrievalCompletedEvent = null;
				}
			}
		}

		#region Public Properties

		public IHtmlPageProcessor[] HtmlProcessors
		{
			get { return this.htmlProcessors; }
		}

		public string ResultHtml { get; private set; }

		public virtual Exception Exception
		{
			get { return this.exception; }
			protected set
			{
				if (value == null)
					throw new ArgumentNullException("value");

				this.exception = value;
			}
		}

		public DateTime? DateStart
		{
			get { return this.workflow.DateStart; }
		}

		public DateTime? DateEnd
		{
			get { return this.workflow.DateEnd; }
		}

		#endregion

		#region Protected Properties

		protected int CurrentStageIndex { get; set; }

		protected IHtmlPageProcessor CurrentProcessor
		{
			get { return (CurrentStageIndex < HtmlProcessors.Length ? HtmlProcessors[CurrentStageIndex] : null); }
		}

		#endregion

		#region Protected Methods

		protected void HandleException(Exception ex)
		{
			Exception = ex;

			this.statementRetrievalCompletedEvent.Set();
			this.callback(this);
		}

		#endregion

		#region Private Fields

		private readonly AsyncCallback callback;
		private readonly IHtmlPageProcessor[] htmlProcessors;
		private readonly IStatementProcessingWorkflow workflow;
		private Exception exception;
		private ManualResetEvent statementRetrievalCompletedEvent = new ManualResetEvent(false);

		#endregion

		#region Implementation of IHtmlPageProcessorFinished

		void IHtmlPageProcessorFinished.Finish(string html)
		{
			ResultHtml = html;

			this.statementRetrievalCompletedEvent.Set();
			this.callback(this);
		}

		#endregion

		#region Implementation of IAsyncResult

		/// <summary>
		/// Gets an indication whether the asynchronous operation has completed.
		/// </summary>
		/// <returns>
		/// true if the operation is complete; otherwise, false.
		/// </returns>
		/// <filterpriority>2</filterpriority>
		public bool IsCompleted
		{
			get { return this.statementRetrievalCompletedEvent.WaitOne(0, false); }
		}

		/// <summary>
		/// Gets a <see cref="T:System.Threading.WaitHandle" /> that is used to wait for an asynchronous operation to complete.
		/// </summary>
		/// <returns>
		/// A <see cref="T:System.Threading.WaitHandle" /> that is used to wait for an asynchronous operation to complete.
		/// </returns>
		/// <filterpriority>2</filterpriority>
		public WaitHandle AsyncWaitHandle
		{
			get { return this.statementRetrievalCompletedEvent; }
		}

		/// <summary>
		/// Gets a user-defined object that qualifies or contains information about an asynchronous operation.
		/// </summary>
		/// <returns>
		/// A user-defined object that qualifies or contains information about an asynchronous operation.
		/// </returns>
		/// <filterpriority>2</filterpriority>
		public object AsyncState
		{
			get { return this.workflow; }
		}

		/// <summary>
		/// Gets an indication of whether the asynchronous operation completed synchronously.
		/// </summary>
		/// <returns>
		/// true if the asynchronous operation completed synchronously; otherwise, false.
		/// </returns>
		/// <filterpriority>2</filterpriority>
		public bool CompletedSynchronously
		{
			get { return false; }
		}

		#endregion

		#region Implementation of IDisposable

		/// <summary>
		/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
		/// </summary>
		/// <filterpriority>2</filterpriority>
		public void Dispose()
		{
			Dispose(true);
			GC.SuppressFinalize(this);
		}

		#endregion
	}
}